package com.hzgj.bcl.soa.ws;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.hzgj.bcl.soa.util.Triple;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class ServiceRelationHandler extends MessageHandler {

    ServiceRelation serviceRelation;
    @Getter
    private String name;

    public ServiceRelationHandler(ServiceRelation serviceRelation) {
        this.serviceRelation = serviceRelation;
    }

    @Override
    void handle(String parameters, BlockingQueue<byte[]> queue) {
        this.name = parameters;
        int split = parameters.indexOf("_");
        if (split > 0) {
            try {
                String service = parameters.substring(0, split);
                TimeUnit.SECONDS.sleep(3L);
                Map<String, Triple<String, String, Long>> triple = serviceRelation.getInvokeTimeByChain(parameters);
                ServiceRelation.Node node = serviceRelation.getNode(service);
                if (node != null) {
                    List<InvokeDetail> nodes = buildChain(node, triple);
                    if (nodes.size() > 0) {
                        queue.put(JSON.toJSONBytes(nodes));
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    List<InvokeDetail> buildChain(ServiceRelation.Node node, Map<String, Triple<String, String, Long>> chain) {
        List<InvokeDetail> nodes = Lists.newArrayList();
        Triple<String, String, Long> triple = getTriple(chain, node.name);
        if (triple != null) {
            if (node.getIns().size() == 0) {
                nodes.add(new InvokeDetail(node.name, triple.getLeft(), triple.getMiddle(), triple.getRight()));
                buildIn(node.getIns(), nodes, chain);
            } else {
                buildIn(node.getIns(), nodes, chain);
                nodes.add(new InvokeDetail(node.name, triple.getLeft(), triple.getMiddle(), triple.getRight()));
            }
            buildOut(node.getOuts(), nodes, chain);
        }
        return nodes;
    }

    private void buildOut(Set<ServiceRelation.Node> outs, List<InvokeDetail> nodes, Map<String, Triple<String, String, Long>> chain) {
        outs.forEach(node -> {
            Triple<String, String, Long> triple = getTriple(chain, node.name);
            if (triple != null) {
                nodes.add(new InvokeDetail(node.name, triple.getLeft(), triple.getMiddle(), triple.getRight()));
            }
            buildOut(node.getOuts(), nodes, chain);
        });
    }

    private void buildIn(Set<ServiceRelation.Node> ins, List<InvokeDetail> nodes, Map<String, Triple<String, String, Long>> chain) {
        ins.forEach(node -> {
            buildIn(node.getIns(), nodes, chain);
            Triple<String, String, Long> triple = getTriple(chain, node.name);
            if (triple != null) {
                nodes.add(new InvokeDetail(node.name, triple.getLeft(), triple.getMiddle(), triple.getRight()));
            }
        });
    }

    Triple<String, String, Long> getTriple(Map<String, Triple<String, String, Long>> chain, String name) {
        Triple<String, String, Long> triple = null;
        for (Map.Entry<String, Triple<String, String, Long>> entry : chain.entrySet()) {
            if (entry.getKey().startsWith(name)) {
                triple = entry.getValue();
                break;
            }
        }
        return triple;
    }

    @AllArgsConstructor
    @Getter
    static class InvokeDetail {
        String name;
        String method;
        String node;
        long span;
    }
}